home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
The World of Computer Software.iso
/
xtndpath.zip
/
XTNDPATH.C
< prev
next >
Wrap
Text File
|
1993-01-12
|
63KB
|
1,490 lines
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <conio.h>
#include <alloc.h>
/*
* Utility: XTNDPATH (eXTeND MS-DOS environment PATH length -- FAST!)
*
* Author: G. David Douglas, Jr. (douglas@usceast.cs.scarolina.edu)
* Date: 1/12/TU/93
* Compiler: Turbo C 1.5 under MS-DOS 4.01, using Compact memory model
*
*
* Usage:
*
* XTNDPATH [-REV] [-PREFIX AlternatePrefixString]
*
* or
*
* XTNDPATH Anything-else (for a help screen to be shown)
*
* All items in brackets are optional and must be specified in the shown
* order; don't use brackets when giving optional items. For example,
* use
*
* XTNDPATH -REV
*
* not
*
* XTNDPATH [-REV]
*
* XTNDPATH will extend the DOS PATH length by appending all environment
* entries that begin with PATHPART (the default PrefixString).
*
* If -REV is specified (MUST BE FIRST PARAMETER), XTNDPATH displays
* information in reverse video (black characters on white background).
*
* If -PREFIX AlternatePrefixString is specified, then the given
* AlternatePrefixString is used instead of the default PATHPART prefix.
*
* Possible ways of giving the XTNDPATH command:
*
*
* XTNDPATH (to simply extend the DOS PATH length by
* appending all environment entries that
* begin with PATHPART )
*
* XTNDPATH -REV (to extend the DOS PATH in the same way
* as when XTNDPATH is used with no
* parameters, but display information
* output in black characters on white
* background.)
*
* XTNDPATH -PREFIX AlternatePrefixString (to extend the DOS PATH length
* by appending all environment
* entries that begin with
* some AlternatePrefixString,
* e.g., PATHPIECE)
*
* XTNDPATH -REV -PREFIX AlternatePrefixString (combines the last two
* given cases)
*
* XTNDPATH anything-else (To display help text directly)
*
* For instance, XTNDPATH HELP
*
*
* This utility, when run, searches through ALL memory blocks allocated
* by MS-DOS for any in use by COMMAND.COM, and, for each COMMAND.COM
* environment block it finds, checks to see if (a) a PATH= entry exists
* there, and (b) if any entries of the form PATHPART= or PATHPART1=,
* PATHPART2=, PATHPARTqwerty=, etc. exist.
*
* The default prefix for appended entries is PATHPART; an alternate
* prefix can be used by giving the -PREFIX option followed by the
* alternate prefix on the command line.
*
* If both conditions (a) and (b) are satisfied, it edits the PATH=
* statement, appending all PATHPARTn= commands to the end of the PATH=
* statement, in the order IN WHICH THEY ARE FOUND, and then displays a
* one-line message showing the location of the environment block modified
* along with the original and new PATH= lengths. In other words, if
*
* PATHPART2=C:\TEMP;C:\TEXTDIR;
* PATH=C:\;C:\DOS;
* PATHPARTQWERTY=C:\QWERTY;
*
* are found, then regardless of the fact that PATHPART2 was found
* before PATHPARTqwerty, PATH= is modified to be
*
* PATH=C:\;C:\DOS;C:\TEMP;C:\TEXTDIR;C:\QWERTY;
*
*
* The same result may be accomplished with
*
* PATHPIECE2=C:\TEMP;C:\TEXTDIR;
* PATH=C:\;C:\DOS;
* PATHPIECEQWERTY=C:\QWERTY;
* XTNDPATH -PREFIX PATHPIECE
*
*
* Note that each original path component, that is, the PATH= component
* or each PATHPARTn= component, is considered a complete path in and
* of itself. However, if there are two path components to be joined, and
* the first one does NOT end with a ';' (semicolon), one will be included
* to keep the resulting extended path valid.
*
* The utility does not consider some characters valid for inclusion in a
* PATH statement; therefore, if any of these are found, they are ignored.
* See the definition of INVALID_PATH_CHARACTERS, found immediately after
* this header comment.
*
* IMPORTANT: It is possible, having run XTNDPATH, for the PATH= statement
* to exceed 255 characters. If, under these conditions, after XTNDPATH
* is run, another PATH= statement is given on the command line, part of
* the previously extended PATH= statement may be ignored by COMMAND.COM,
* producing an extra, un-deletable, environment entry. For this reason,
* XTNDPATH checks for COMMAND.COM environment entries which contain no
* '=' character, and deletes them. This is safe because COMMAND.COM allows
* no environment entry to be created without the use of an '=' character.
* XTNDPATH will do this correction, WITH OR WITHOUT a PATH= entry and any
* PATHPARTn= entries in all found COMMAND.COM environment blocks.
*
* Also note: after the utility is done editing the PATH= statement,
* all PATHPARTn= statements are deleted, and are NOT present in the
* final modified environment. The PATH= entry retains its original
* relative position in the modified environment's entries.
*
* Final note: for safety reasons, ONLY environment areas directly
* used by some copy of COMMAND.COM might be modified; all others are
* ignored. This is because I cannot be sure that some utilities don't
* directly modify their environment areas.
*
* This utility was inspired in part by the BIGPATH utility, written by
* Brian Moran, brian@mirror.TMC.COM . It has been tested successfully
* in extending the PATH= environment entry to over 1200 characters, after
* which the search for executables by COMMAND.COM is STILL successful!
*
*
* THIS SOURCE CODE IS ORIGINALLY (C)OPYRIGHT 1993 DAVID DOUGLAS,
* douglas@usceast.cs.scarolina.edu . IT IS HEREBY PLACED IN THE PUBLIC
* DOMAIN AS FAR AS FURTHER USE AND MODIFICATION IS CONCERNED. (THIS NOTICE
* MUST BE INCLUDED IN ANY MODIFIED VERSION OF THIS SOURCE CODE.) USE THIS
* SOURCE CODE AT YOUR OWN RISK; ALL STANDARD DISCLAIMERS APPLY. IT WORKS
* EXTREMELY WELL FOR ME; I HOPE YOU FIND IT AS USEFUL AS I HAVE.
*/
#define NOT_FINAL_MCB_CODE 0x4D /* 'M' */
#define FINAL_MCB_CODE 0x5A /* 'Z' */
#define BYTES_PER_PARAGRAPH 16
#define MAX_MCB_NAME_LEN 8
#define INVALID_PATH_CHARACTERS "*+|=,?\"<> []/"
#define DEFAULT_PREFIX_STRING "PATHPART" /* IMPORTANT! DON'T USE */
/* ANY '=' CHARACTERS!!! */
typedef struct {
unsigned char mcb_entry_code; /* Last, or not last MCB? */
unsigned int mcb_psp_segment; /* Segment of Program- */
/* Segment-Prefix with */
/* which this Memory */
/* Control Block is */
/* associated */
unsigned int mcb_size; /* Size of Memory Control */
/* Block area in segments */
/* (16-byte paragraphs) */
unsigned char
unknown_mcb_info[3]; /* Three bytes in Memory */
/* Control Block regarding */
/* whose purpose I have no */
/* information. */
unsigned char mcb_name
[MAX_MCB_NAME_LEN]; /* Possible program name */
/* associated with Memory */
/* Control Block */
} mem_control_block; /* Definition for Memory Control Block */
/*****************************************************************************/
/************************** XTNDPATH - MAIN PROGRAM **************************/
/*****************************************************************************/
main(argc, argv)
int argc;
char *argv[];
{
int i; /* Loop variable */
struct REGPACK cpu_registers;
unsigned int *word_pointer;
unsigned char *byte_pointer;
mem_control_block *mcb_pointer,
*mcb_pointer_copy,
*environment_mcb_pointer;
unsigned char *environment_block_pointer;
unsigned char *prefix_string,
*prefix_string_copy;
int prefix_string_length;
unsigned char *source_string_ptr,
*destination_string_ptr;
char program_name
[MAX_MCB_NAME_LEN+1]; /* One more entry for NULL char */
int reverse_video_flag; /* Display modification */
/* information in black */
/* characters on white */
/* background, or not? (1 or 0) */
int nostop(void); /* "Don't stop" routine */
/* to be used with the builtin */
/* ctrlbrk routine. */
void modify_environment(unsigned char *,
unsigned int,
unsigned char *,
int); /* Program environment */
/* modification routine. */
void usage(void); /* Routine to tell user how to */
/* use XTNDPATH. */
void *emalloc(unsigned int); /* Memory allocation and */
void efree(void *); /* freeing routines. */
/*
* First, set things up so that <Control>C or <Control><Break>
* won't interrupt anything.
*/
ctrlbrk(nostop);
/*
* Let the reverse_video_flag variable default to a value of 0
* (use current text attributes for displaying environment
* modification information). This value will be changed if
* -REV is the first parameter on the command line; in that
* case, environment modification information will displayed using
* black characters on a white background.
*/
reverse_video_flag = 0;
/*
* Now see if the user gave any command line parameters. If there
* are any, and the first parameter is not -REV or -PREFIX, then
* assume the user wants help and call the usage procedure to
* display a description of what this utility does, and exit.
*
* Otherwise:
*
* See if the first given parameter (uppercased) is -REV. If so,
* then set the reverse_video_flag to 1 to indicate to the
* modify_environment procedure that modification information output
* is to be displayed in black characters on white background. If
* the first given parameter is -REV, delete it from the command
* parameters.
*
* Now see if the current first parameter (uppercased) is -PREFIX.
* If so, then use the next parameter (if given; if not, use PATHPART)
* in place of PATHPART as the prefix for appended entries, UNLESS
* the new prefix string is PATH; in this case, default to PATHPART.
*/
if(argc > 1)
{
strupr(argv[1]); /* Convert first parameter */
/* to uppercase. */
if((strcmp(argv[1], "-REV") != 0) &&
(strcmp(argv[1], "-PREFIX") != 0)) /* Display help information */
{
usage();
return(0);
}
else
{
/*
* If first parameter is -REV, set reverse_video_flag to
* 1, and delete the first parameter from the parameter list.
*/
if(strcmp(argv[1], "-REV") == 0)
{
reverse_video_flag = 1;
efree((void *)argv[1]);
for(i = 1; i < argc - 1; i++)
argv[i] = argv[i+1];
argc--;
}
/*
* Check current first parameter. If it is -PREFIX, process
* it and the next (possible) parameter appropriately.
* Otherwise, since current first parameter is not -REV or
* -PREFIX, and the user help case was tested for earlier,
* use the default prefix string.
*/
/*
* In case any given "-REV" parameter was deleted earlier,
* uppercase the current first parameter (maybe again).
*/
strupr(argv[1]);
if(strcmp(argv[1],
"-PREFIX") == 0) /* Check out alternate prefix */
{
if(argc > 2) /* Use parameter 2 as prefix, */
/* and assume (for now) user */
/* knows what he's doing in */
/* choosing characters that */
/* make up an alternate */
/* prefix. */
{
/*
* Use the alternate prefix, UNLESS it is PATH or PATH=,
* which is not safe, since we would be appending all
* entries with PATH to the PATH= entry, which doesn't
* make sense. In that case, use the default prefix
* instead.
*/
strupr(argv[2]); /* Convert prefix string to uppercase */
if((strcmp(argv[2], "PATH") != 0) &&
(strcmp(argv[2], "PATH=") != 0))
{
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(argv[2]) + 1));
strcpy(prefix_string, argv[2]);
}
else
{
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(DEFAULT_PREFIX_STRING) + 1));
strcpy(prefix_string, DEFAULT_PREFIX_STRING);
strupr(prefix_string); /* Convert to uppercase, */
/* just in case */
}
}
else
{ /* No 2nd parameter given -- use default prefix */
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(DEFAULT_PREFIX_STRING) + 1));
strcpy(prefix_string, DEFAULT_PREFIX_STRING);
strupr(prefix_string); /* Convert to uppercase, */
} /* just in case */
}
else
{ /* Use default prefix string */
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(DEFAULT_PREFIX_STRING) + 1));
strcpy(prefix_string, DEFAULT_PREFIX_STRING);
strupr(prefix_string); /* Convert to uppercase, */
/* just in case */
}
}
}
else
{
/*
* Otherwise, no arguments were given. Use the default prefix
* string.
*/
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(DEFAULT_PREFIX_STRING) + 1));
strcpy(prefix_string, DEFAULT_PREFIX_STRING);
strupr(prefix_string); /* Convert to uppercase, just in case */
}
/*
* Final safety check -- now that the default prefix string has
* been assigned a "value", see if any '=' characters exist in
* the prefix string. If one or more '=' characters are found,
* delete them. If, in doing this deletion, the prefix string
* becomes an empty string (originally contained all '=' characters),
* use the default prefix string instead.
*
* This final safety check is done because COMMAND.COM uses the '='
* character as a delimiter in environment entries, and doesn't even
* allow more than one '=' character to be used in a 'SET' command
* (which of the two or more '=' characters would be the delimiter?).
*/
if(strchr(prefix_string, (int)'=') != NULL)
{
prefix_string_copy = (unsigned char *)
emalloc((unsigned int)(strlen(prefix_string) + 1));
source_string_ptr = prefix_string;
destination_string_ptr = prefix_string_copy;
while(*source_string_ptr != (unsigned char)NULL)
{
if(*source_string_ptr != '=')
{
*destination_string_ptr = *source_string_ptr;
source_string_ptr++;
destination_string_ptr++;
}
else source_string_ptr++;
}
*destination_string_ptr = (unsigned char)NULL;
if(strlen(prefix_string_copy) != 0) /* Prefix string now valid - */
/* copy it back to original */
{
strcpy(prefix_string, prefix_string_copy);
efree((void *)prefix_string_copy);
}
else
{ /* Prefix string was all '=' characters -- use default */
efree((void *)prefix_string_copy);
efree((void *)prefix_string);
prefix_string = (unsigned char *)emalloc((unsigned int)
(strlen(DEFAULT_PREFIX_STRING) + 1));
strcpy(prefix_string, DEFAULT_PREFIX_STRING);
strupr(prefix_string); /* Convert to uppercase, just in case */
}
}
/*
* Prefix string setup is now done.
*
* Issue a PC-DOS service number 52h call to retrieve the
* segment and offset of the last location "officially"
* occupied by DOS. Segment will be contained in the ES
* pseudo-register, offset in the BX pseudo-register. Subtract
* 2 from BX to get offset of the 2 bytes containing segment number
* of the first Memory Control Block (MCB).
*/
cpu_registers.r_ax = 0x5200; /* AH register <- 0x52, */
/* AL register <- 0x00 */
intr(0x21, &cpu_registers);
cpu_registers.r_bx -= 2;
/*
* Load segment and offset into pointer variable -- r_es and
* r_bx pseudo_registers (as well as actual registers ES and
* BX) are 16 bits in width, so shift ES component left 16
* bits before including the offset contained in BX.
*/
word_pointer = (unsigned int *)(((long)cpu_registers.r_es << 16) |
(long)cpu_registers.r_bx);
/*
* Now, retrieve the segment value pointed to by word_pointer,
* and use it along with an offset of 0 as the starting Memory
* Control Block address. Use a character pointer for
* initially checking Memory Control Blocks, since only the
* first byte in the 16-byte Memory Control Block header
* determines whether this is one of the starting blocks
* (value 4Dh ('M')) or the last block (value 5Ah ('Z')).
*/
word_pointer = (unsigned int *)(((long)*word_pointer << 16) | 0x0000);
byte_pointer = (unsigned char *)word_pointer;
do
{
/*
* Get a Memory-Control-Block pointer pointing to this
* location, for easier referencing of its contents.
*/
mcb_pointer = (mem_control_block *)byte_pointer;
/*
* Apparently, under MS-DOS, only actual program code immediately
* follows (in memory) the Memory Control Block header containing
* that program code's starting segment. Therefore, only
* check memory control blocks for which this applies.
*
* For any such found Memory Control Block, if the name field
* contains "COMMAND" followed by a null (ASCII 0) character,
* look at offset 2Ch for the segment of that code's environment
* memory block. Then check in the Memory Control Block of the
* environment memory block. If the entry indicating which
* Program Segment Prefix it is associated with matches the
* starting code segment of the earlier found COMMAND.COM
* memory area, then we have a valid environment area to check
* and possibly modify.
*/
if((mcb_pointer->mcb_psp_segment != 0) && /* NOT FREE in MS-DOS */
(((unsigned int)((long)mcb_pointer >> 16) + 1) ==
mcb_pointer->mcb_psp_segment))
{
for(i = 0; i < MAX_MCB_NAME_LEN; i++)
{
program_name[i] = mcb_pointer->mcb_name[i];
if(program_name[i] == (unsigned char) 0)
break;
}
program_name[MAX_MCB_NAME_LEN] = (char)0;
if(strcmp(program_name, "COMMAND") == 0)
{
/*
* Check on the environment memory block for this program.
* Skip down 16 bytes to the actually memory block this
* Memory Control Block references, then look in location
* (more specifically, at offset) 2Ch for the segment of
* the corresponding memory control block. If this value is
* zero, then this program doesn't use an environment
* memory block, so don't worry about it. Otherwise, check
* the Memory Control Block corresponding to this possible
* environment memory block. If the Program Segment Prefix
* field in the MCB, i.e., the PSP with which this memory
* block is associated, matches the PSP of the current
* COMMAND memory block being checked, then this IS a valid
* environment memory block, and can safely be modified.
*/
mcb_pointer_copy =
(mem_control_block *)
((((unsigned long)mcb_pointer >> 16) + 1) << 16);
word_pointer = (unsigned int *)
(((unsigned long)mcb_pointer_copy) | (unsigned long)0x002C);
environment_mcb_pointer = (mem_control_block *)
((((unsigned long)*word_pointer - 1) << 16) | 0x0000);
if((*word_pointer != 0) &&
(mcb_pointer->mcb_psp_segment ==
environment_mcb_pointer->mcb_psp_segment))
{
/*
* Get "byte" pointer to start of environment memory
* block, then call environment modification routine
* with this pointer, with the memory block size
* contained in the environment block Memory Control
* Block, with the environment entry prefix string
* for environment entries to be appended to the
* environment PATH entry, and with the reverse_video_flag
* variable. The reverse_video_flag variable's value
* indicates (value 0) modification information output is
* to be done using current text attributes, or (value
* non-zero) modification information output is to be done
* using black characters on white background.
*/
environment_block_pointer =
(unsigned char *)(((unsigned long)*word_pointer << 16) | 0x0000);
modify_environment(environment_block_pointer,
environment_mcb_pointer->mcb_size * BYTES_PER_PARAGRAPH,
prefix_string,
reverse_video_flag);
}
}
}
/*
* Now, if byte_pointer doesn't already point to the last Memory
* Control Block, advance to the next Memory Control Block -- take
* the segment portion of the MCB pointer, add in the number of
* segments that the Memory Control Block occupies, then
* add 1 more to advance to the beginning of the next
* Memory Control Block.
*/
if(*byte_pointer != (unsigned char)FINAL_MCB_CODE)
byte_pointer = (unsigned char *)
((((long)byte_pointer >> 16) + mcb_pointer->mcb_size + 1) << 16);
}
while(*byte_pointer != (unsigned char)FINAL_MCB_CODE);
/*
* All memory blocks have been checked. Now free the memory
* occupied by the prefix-string, and exit the utility.
*/
efree((void *)prefix_string);
return(0);
} /* End of XTNDPATH main program */
/*****************************************************************************/
/*
* Function nostop
*
* This function is used along with the builtin ctrlbrk routine. This
* function, by returning a non-zero value when called by the internal
* code of ctrlbrk, indicates that <Ctrl>C or <Ctrl><Break> is NOT to
* interrupt the program, but to allow execution to continue.
*/
int nostop(void)
{
return(1);
} /* End of function nostop */
/*****************************************************************************/
/*
* Procedure modify_environment
*
* This procedure, given
*
* (1) a character pointer to the start of a program environment area,
*
* (2) the size of the environment in bytes,
*
* (3) the prefix string for entries to be appended to the PATH= entry
* (hereafter referred to simply as PATHPARTn), and
*
* (4) a reverse-video-flag: 0 value indicates that modification information
* output is to be done with current text attributes, non-zero value
* indicates that modification information output is to be done using
* black characters on white background,
*
* reads all environment strings into separate dynamically allocated memory
* areas, then appends all found PATHPARTn entries to the PATH entry. All
* PATHPARTn entries are then, as far as the final environment is concerned,
* deleted. The modified environment, with the changed PATH statement, and
* all other appropriate environment entries, is written back out to the
* original environment area, with the PATH entry in its original relative
* position. After the environment has been modified, the procedure displays
* a one-line message, giving the memory location of the modified environment
* memory block along with the original and new PATH lengths.
*
* Note that individual environment entries are terminated by an ASCII 0
* character (End-of-string); the end of the environment area is signaled
* by one ASCII 0 character, which terminates the final environment entry,
* followed by one more ASCII 0 character. If the environment is empty,
* then the environment area has an ASCII 0 character at the very beginning.
*
* IMPORTANT: This procedure also checks for invalid environment entries
* in the referenced memory block. By invalid, I mean that an
* entry does not have an '=' character in it, which COMMAND.COM
* requires when an environment entry is created. Any such
* entries are deleted, and the procedure notifies the user.
* This environment "repair" is done whether or not a PATH=
* entry and one or more PATHPARTn= entries are found in the
* environment.
*/
void modify_environment(environment_pointer,
environment_size,
prefix_string,
reverse_video_flag)
unsigned char *environment_pointer;
unsigned int environment_size;
unsigned char *prefix_string;
int reverse_video_flag;
{
int i, j; /* Loop variables */
unsigned char *saved_environment_pointer;
unsigned char *old_environment_pointer;
unsigned int old_environment_byte_count;
unsigned int environment_byte_count;
unsigned char **environment_strings; /* Array of environment */
/* strings, dynamically */
/* allocated. */
unsigned char *environment_string; /* Environment string, */
/* dynamically */
/* allocated. */
unsigned int environment_string_count;
unsigned char *string_ptr;
int path_string_index; /* Array index of PATH= */
/* entry in */
/* environment_strings */
int *environment_string_indexes; /* Array of indices to */
/* be used when doing */
/* final environment */
/* modification; all */
/* environment entries */
/* are accounted for, */
/* except for any */
/* PATHPARTn= entries. */
int environment_string_index_max;
int *other_environment_indexes; /* Indices of */
/* environment entries */
/* which contain */
/* PATHPARTn= entries. */
int original_path_length;
int other_environment_index_max;
int invalid_environment_entry_count;
unsigned char *new_path_string;
int new_path_length;
unsigned char *new_path_ptr; /* Pointer to current */
/* position in new */
/* PATH= string */
unsigned char *path_entry_ptr; /* Pointer to current */
/* position in */
/* environment entry */
/* being included in */
/* new PATH= string */
unsigned char last_character, /* Last character put */
current_character; /* into the (updated) */
/* PATH= environment */
/* entry, and current */
/* character being */
/* considered for */
/* inclusion in the */
/* (updated) PATH= */
/* environment entry. */
int prefix_string_length;
void *emalloc(unsigned); /* Memory allocation, */
void *erealloc(void *, unsigned); /* re-allocation, and */
void efree(void *); /* freeing routines. */
/*
* Initialize all count and applicable pointer variables.
*/
environment_byte_count = 0;
environment_string_count = 0;
prefix_string_length = strlen(prefix_string);
/*
* These variables are initialized to make it easier to decide
* later whether or not they were ever given a value from emalloc
* or erealloc.
*/
environment_strings = (unsigned char **)NULL;
environment_string_indexes = (int *)NULL;
other_environment_indexes = (int *)NULL;
/*
* Save the environment pointer value so it can be used again
* when the possibly modified environment is written out
* to its original memory area.
*/
saved_environment_pointer = environment_pointer;
/*
* Try to process the environment area, but only if it
* actually has environment entries.
*/
if(*environment_pointer != (unsigned char)0)
{
/*
* Process environment entries until an ASCII 0 byte is
* found following the last processed environment string,
* or until all bytes in the environment memory block
* have been processed.
*/
while((*environment_pointer != (unsigned char)0) &&
(environment_byte_count < environment_size))
{
/*
* Count the number of bytes in the next environment
* string, then allocate enough memory for that many
* bytes for a string in a separate area, and copy the
* environment entry to that separate area.
*/
old_environment_byte_count = environment_byte_count;
old_environment_pointer = environment_pointer;
while((*environment_pointer != (unsigned char)0) &&
(environment_byte_count < environment_size))
{
environment_pointer++;
environment_byte_count++;
}
/*
* Add 1 to environment_byte_count to account for last
* ASCII 0 character, if that's what environment_pointer
* currently points to.
*/
if(*environment_pointer == (unsigned char)0)
environment_byte_count++;
environment_string_count++;
environment_strings = (unsigned char **)
erealloc((unsigned int *)environment_strings,
(environment_string_count) * sizeof(unsigned char *));
environment_string = (unsigned char *)emalloc((unsigned int)
((environment_byte_count - old_environment_byte_count) *
sizeof(unsigned char)));
environment_strings[environment_string_count-1] =
environment_string;
string_ptr = environment_string;
/*
* Copy the current environment entry to its corresponding
* work area, and terminate it with an ASCII 0 character.
*/
while(old_environment_pointer != environment_pointer)
{
*string_ptr = *old_environment_pointer;
string_ptr++;
old_environment_pointer++;
}
*string_ptr = (unsigned char)0;
environment_pointer++;
}
/*
* Allocate enough memory for index arrays. Then, search
* through environment strings for one beginning with 'PATH='.
* If an environment entry is found which is not of the form
* PATH= or PATHPARTn=, then simply add its index to the
* environment index list, UNLESS IT CONTAINS NO '=' CHARACTER;
* in this case, IGNORE IT. Otherwise, if the string is of the
* form PATH=, add its index to the environment index list,
* and record its index in the path_string_index variable.
* On the other hand, if it is of the form PATHPARTn=, add its
* index to the other environment index list.
*
* If a PATH= entry was found, then append all PATHPARTn= entries
* to it; otherwise, don't even bother to try modifying the
* environment. The exception to this rule is if any invalid
* (contain no '=' character) entries were found; in this case,
* the modified environment WILL be written back to its original
* area, with the invalid entries deleted.
*/
environment_string_indexes = (int *)emalloc((unsigned int)
(environment_string_count * sizeof(int)));
other_environment_indexes = (int *)emalloc((unsigned int)
(environment_string_count * sizeof(int)));
environment_string_index_max = -1;
other_environment_index_max = -1;
path_string_index = -1;
for(i = 0; i < environment_string_count; i++)
{
if(strncmp("PATH=", environment_strings[i], 5) == 0)
{
path_string_index = i;
environment_string_index_max++;
environment_string_indexes[environment_string_index_max] = i;
}
else
{
if(strncmp(prefix_string,
environment_strings[i],
prefix_string_length) == 0)
{
/*
* Check for invalid environment entry, even in this
* case.
*/
string_ptr = environment_strings[i];
while((*string_ptr != (unsigned char)0) &&
(*string_ptr != '='))
string_ptr++;
if(*string_ptr == '=') /* Valid entry -- record it */
{
other_environment_index_max++;
other_environment_indexes[other_environment_index_max]
= i;
}
}
else
{
/*
* Check for invalid environment entry. If the entry
* is valid, however, record it in the main environment
* index list.
*/
string_ptr = environment_strings[i];
while((*string_ptr != (unsigned char)0) &&
(*string_ptr != '='))
string_ptr++;
if(*string_ptr == '=') /* Valid entry -- record it */
{
environment_string_index_max++;
environment_string_indexes
[environment_string_index_max] = i;
}
}
}
}
/*
* If a PATH= entry was found, and one or more PATHPARTn= entries
* were found, then append all PATHPARTn= entries, in the order
* in which they were found, to the PATH= entry, in another
* memory work area. Then, delete the copy of the original
* PATH= string, and replace it with the new one.
*/
if((path_string_index != -1) &&
(other_environment_index_max != -1))
{
/*
* First, allocate enough memory for a string that can hold
* the entire final 'PATH=' environment entry, including
* any invalid characters it might have (which will be
* deleted later).
*/
new_path_length = strlen(environment_strings
[path_string_index]);
for(i = 0; i <= other_environment_index_max; i++)
new_path_length +=
strlen(environment_strings[other_environment_indexes[i]]);
new_path_string = (unsigned char *)
emalloc((unsigned int)((new_path_length + 1) * sizeof(unsigned char)));
new_path_ptr = new_path_string;
/*
* Now copy the PATH= and all PATHPART environment entries to
* the new string. Copy 'PATH=' directly, then ignore invalid
* characters. For all PATHPART entries, ignore everything
* until after the first '=' character, then ignore invalid
* characters. In all cases, try to keep the directory-name-
* separators '\' and ';' path entry separators, as well as
* the ':' drive name indicator correctly used. (For instance,
* you shouldn't have '\\' or ';;' or '::' in a PATH=
* statement; this is the extent of this kind of error
* correction.) Note that this parsing isn't the smartest
* thing around, but it should hold down most errors. Also
* note that this parsing automatically places a ';' between
* each included PATH= component (previously in PATH= or
* PATHPARTn=) unless a ';' is already present.
*/
last_character = (unsigned char)0;
current_character = (unsigned char)0;
path_entry_ptr = environment_strings[path_string_index];
/*
* Copy the PATH= piece of the original PATH= entry, and
* move just beyond it.
*/
for(j = 0; j < 5; j++)
{
*new_path_ptr = *path_entry_ptr;
new_path_ptr++;
path_entry_ptr++;
}
/*
* Copy the rest of the original PATH= entry, ignoring
* invalid characters and character combinations.
*/
while(*path_entry_ptr != (unsigned char)0)
{
/*
* Check out the next character available for inclusion
* in the PATH= statement. If it is invalid, ignore it,
* otherwise check it further.
*/
if(strchr(INVALID_PATH_CHARACTERS,
(int)*path_entry_ptr) == NULL)
{
/*
* Character was not invalid, so now eliminate
* anything like '\\' or ';;' or '::'.
*/
current_character = *path_entry_ptr;
if(!((current_character == last_character) &&
((current_character == '\\') ||
(current_character == ';') ||
(current_character == ':'))
)
)
{
*new_path_ptr = current_character;
last_character = current_character;
new_path_ptr++;
}
}
/*
* Regardless of whether or not the character just
* then considered was valid, move the path entry
* pointer to the next character to be considered.
*/
path_entry_ptr++;
}
/*
* Now append all PATHPARTn= environment entries, ignoring
* invalid characters and character combinations.
*/
for(i = 0; i <= other_environment_index_max; i++)
{
path_entry_ptr =
environment_strings[other_environment_indexes[i]];
/*
* Skip to just beyond the first '=' in the string.
* Only include this entry if an '=' is found. (This
* check really isn't necessary, since it was determined
* earlier that any entry with an index in the
* other_environment_indexes list DOES have an '='
* character in it.)
*/
while((*path_entry_ptr != (unsigned char)0) &&
(*path_entry_ptr != '='))
path_entry_ptr++;
if(*path_entry_ptr != '=')
/* Skip this environment entry completely */
continue; /* Continue at the end of the for loop */
else path_entry_ptr++;
/*
* If the last character included in the modified PATH=
* statement was not a ';', include a ';' to separate the
* included components correctly.
*/
if(last_character != ';')
{
*new_path_ptr = ';';
new_path_ptr++;
last_character = ';';
}
/*
* Process each character in the environment entry until
* an ASCII 0 character is found.
*/
while(*path_entry_ptr != (unsigned char)0)
{
/*
* Check out the next character available for inclusion
* in the PATH= statement. If it is invalid, ignore it,
* otherwise check it further.
*/
if(strchr(INVALID_PATH_CHARACTERS,
(int)*path_entry_ptr) == NULL)
{
/*
* Character was not invalid, so now eliminate
* anything like '\\' or ';;' or '::'.
*/
current_character = *path_entry_ptr;
if(!((current_character == last_character) &&
((current_character == '\\') ||
(current_character == ';') ||
(current_character == ':'))
)
)
{
*new_path_ptr = current_character;
last_character = current_character;
new_path_ptr++;
}
}
/*
* Regardless of whether or not the character just
* then considered was valid, move the path entry
* pointer to the next character to be considered.
*/
path_entry_ptr++;
}
}
/*
* Terminate new_path_string with an ASCII 0 character.
*/
*new_path_ptr = (unsigned char)0;
/*
* The new PATH= string is complete. Now save the length
* of the original PATH= entry for later use, free the original
* entry from memory, and replace it with the new PATH= entry
* in the environment strings list.
*/
original_path_length = strlen(environment_strings
[path_string_index]);
efree((void *)environment_strings[path_string_index]);
environment_strings[path_string_index] = new_path_string;
}
/*
* The modified environment is ready to be written back to
* the original environment area, if necessary.
*
* Two possible conditions exist where the environment needs
* to be written back to the original area:
*
* (a) PATH= entry and one or more PATHPARTn= entries existed
* in the original environment, and
*
* (b) one or more invalid environment entries were found.
*
*
* Condition (b) is checked as follows:
*
* The original number of environment entries, minus the
* number of PATHPARTn= entries in the original environment,
* should equal the number of entries in the modified
* environment. If, however, when the real number of entries
* in the modified environment is subtracted from the correct
* number of modified environment entries, a non-zero value
* is found, then there were invalid environment entries.
* This last number gives the number of invalid environment
* entries.
*/
invalid_environment_entry_count = environment_string_count -
(other_environment_index_max + 1) -
(environment_string_index_max + 1);
if(
((path_string_index != -1) &&
(other_environment_index_max != -1)) ||
(invalid_environment_entry_count != 0)
)
{
/*
* Copy the final modified environment strings to the original
* environment area. Separate each environment entry with an
* ASCII 0 character, and terminate the environment area with
* one extra final ASCII 0 character.
*/
environment_pointer = saved_environment_pointer;
for(i = 0; i <= environment_string_index_max; i++)
{
string_ptr =
environment_strings[environment_string_indexes[i]];
while((*environment_pointer = *string_ptr)
!= (unsigned char)0)
{
environment_pointer++;
string_ptr++;
}
environment_pointer++;
}
*environment_pointer = (unsigned char) 0;
/*
* Now report the environment PATH modification, with
* segment:offset of environment block. If the modification
* was due to a PATH= entry modification, report the original
* and new PATH sizes. Also, if the modification was due
* to invalid environment entries being deleted, report that
* as well. Two (c)printf statements are used, because, for
* some reason, in this version of Turbo C, there's some
* subtle problem printing, at least using the %d format,
* after %4.4X is used twice (I think -- the original cprintf
* statement looked okay to me! :-).
*
* The value of the reverse_video_flag parameter determines
* whether (0 value) information is to be given using current
* text attributes, or (non-zero value) information is to be
* given using black characters on white background.
*/
if(reverse_video_flag != 0)
{
textcolor(BLACK); /* Both used in conjunction with cprintf */
textbackground(WHITE);
}
if((path_string_index != -1) &&
(other_environment_index_max != -1))
{
cprintf(" COMMAND environment at %4.4X:%4.4X : ",
((unsigned long)saved_environment_pointer >> 16),
((unsigned long)saved_environment_pointer & 0xFFFF));
cprintf("PATH length changed from %d to %d.",
original_path_length,
strlen(environment_strings[path_string_index]));
printf("\n");
}
if(invalid_environment_entry_count != 0)
{
cprintf(" COMMAND environment at %4.4X:%4.4X : ",
((unsigned long)saved_environment_pointer >> 16),
((unsigned long)saved_environment_pointer & 0xFFFF));
if(invalid_environment_entry_count == 1)
cprintf("invalid environment entry deleted.");
else cprintf("invalid environment entries deleted.");
printf("\n");
}
}
/*
* Now free up all memory used by any applicable environment
* string areas and index array areas.
*/
if(environment_strings != (unsigned char **)NULL)
{
for(i = 0; i < environment_string_count; i++)
efree((void *)environment_strings[i]);
efree((void *)environment_strings);
}
if(environment_string_indexes != (int *)NULL)
efree((void *)environment_string_indexes);
if(other_environment_indexes != (int *)NULL)
efree((void *)other_environment_indexes);
}
} /* End of procedure modify_environment */
/*****************************************************************************/
/*
* Procedure abort_utility
*
* This procedure, given an exit code, currently calls the builtin exit
* routine to exit the program. It is used to simplify localizing all
* utility shutdown routine calls.
*/
void abort_utility(error_code)
int error_code;
{
extern void exit(int);
exit(error_code);
} /* End of procedure abort_utility */
/*****************************************************************************/
/*
*
* Function emalloc
*
* This function augments the builtin malloc routine. Given the amount
* of memory desired for a pointer assignment, it calls malloc with that
* value, and checks the pointer value returned. If the returned pointer
* is NULL, the function displays an error message and shuts down the
* utility by calling the abort_utility procedure. Otherwise, it returns
* the pointer returned by malloc.
*/
void *emalloc(n)
unsigned n;
{
void *p;
void abort_utility(int);
/* void *malloc(unsigned); */
p = (void *)malloc(n);
if (p == NULL)
{
fprintf(stderr,"\n\nError in malloc -- out of memory.");
fprintf(stderr,"\nUtility aborted.\n\n");
abort_utility(1);
}
return(p);
} /* End of function emalloc */
/*****************************************************************************/
/*
*
* Function erealloc
*
* This function augments the builtin realloc routine. Given a pointer,
* along with the new amount of memory, which, after being allocated, the
* pointer's referenced area is to be copied to, it calls realloc with the
* pointer and new memory area size. If the pointer returned by realloc
* is NULL, the function displays an error message and shuts down the utility
* by calling the abort_utility procedure. Otherwise, it returns the
* pointer returned by realloc.
*
*/
void *erealloc (ptr, newsize)
void *ptr;
unsigned newsize;
{
void *p;
void abort_utility(int);
/* void *realloc(void *, unsigned); */
p = (void *)realloc(ptr, newsize);
if (p == NULL)
{
fprintf(stderr,"\n\nError in realloc -- out of memory.");
fprintf(stderr,"\nUtility aborted.\n\n");
abort_utility(1);
}
return (p);
} /* End of function erealloc */
/*****************************************************************************/
/*
*
* Procedure efree
*
* This procedure, given a pointer, calls the builtin free routine to
* free-up the memory pointed to by that pointer.
*
*/
void efree(p)
void *p;
{
/* extern void free(void *); */
free(p);
} /* End of procedure efree */
/*****************************************************************************/
/*
* Procedure usage
*
* This procedure gives the user a description of what the XTNDPATH utility
* does and how to use it.
*/
void usage(void)
{
printf(
"XTNDPATH - (eXTeND PATH): XTNDPATH [-REV] [-PREFIX Alternate-Prefix]\n"
" (bracketed items are optional; don't include [] when specifying)\n"
" Extends DOS path command by appending all DOS environment entries\n"
" that start with PATHPART to the PATH entry. XTNDPATH can\n"
" modify all COMMAND.COM environment areas it finds in memory, even\n"
" deleting invalid (no '=' character) environment entries.\n"
"\n"
" Set up your path in the usual way, and specify the remaining paths\n"
" (to be included) in one or more environment entries, each\n"
" beginning with PATHPART (the default Prefix). All appended\n"
" path-part entries are deleted from the final modified environment.\n"
" For example, enter these lines at the COMMAND prompt:\n"
" SET PATHPARTqwerty=C:\\UTILITY\n"
" PATH=C:\\;C:\\DOS;\n"
" SET PATHPART = C:\\TEMP\n"
" XTNDPATH\n"
" SET\n"
"\n"
" One line of the SET command output should look like the following:\n"
"\n"
" PATH=C:\\;C:\\DOS;C:\\UTILITY;C:\\TEMP\n"
"\n"
" Written by David Douglas : douglas@usceast.cs.scarolina.edu\n");
} /* End of procedure usage */
/*****************************************************************************/
/******************** End of XTNDPATH utility source code ********************/
/*****************************************************************************/